package org.example.repository.dialect.memory;

import org.example.repository.*;
import org.example.repository.condition.BinaryOperatorCondition;
import org.example.repository.condition.ConjectionCondition;
import org.example.repository.condition.ExpressionCondition;
import org.example.repository.condition.UnaryConjectionCondition;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import java.util.Comparator;
import java.util.function.Predicate;

import static org.example.repository.Condition.OP_EQUAL;
import static org.example.repository.Condition.OP_GTE;

class QueryFilter<T extends Entity<?>> implements Predicate<T> {

    private static final Comparator<Object> comparator = new DefaultObjectComparator();

    private final Schema schema;
    private final Query query;

    public QueryFilter(Schema schema, Query query) {
        this.schema = schema;
        this.query = query;
    }

    @Override
    public boolean test(T x) {
        if (query == null) return true;
        if (query.where() == null) return true;
        BeanWrapperImpl bw = new BeanWrapperImpl();
        bw.setBeanInstance(x);
        return testAnyCondition(bw, query.where());
    }

    private boolean testAnyCondition(BeanWrapper bw, Condition condition) {
        if (condition instanceof BinaryOperatorCondition boc) {
            return testBinaryOperatorCondition(bw, boc);
        } else if (condition instanceof ConjectionCondition cc) {
            return testConjectionCondition(bw, cc);
        } else if (condition instanceof UnaryConjectionCondition ucc) {
            return testUnaryConjectionCondition(bw, ucc);
        } else if (condition instanceof ExpressionCondition ec) {
            return testExpressionCondition(bw, ec);
        } else {
            throw new UnsupportedOperationException("not supported condition: " + condition);
        }
    }

    private boolean testBinaryOperatorCondition(BeanWrapper bw, BinaryOperatorCondition boc) {
        String op = boc.getOp();
        String name = boc.getProperty();
        Object value = bw.getPropertyValue(name);
        if (OP_EQUAL.equals(op)) {
            return comparator.compare(value, boc.getValue()) == 0;
        } else if (OP_GTE.equals(op)) {
            return comparator.compare(value, boc.getValue()) >= 0;
        }
        return false;
    }

    private boolean testConjectionCondition(BeanWrapper bw, ConjectionCondition cc) {
        if (cc.getChildren().isEmpty()) {
            return true;
        }
        if (Condition.LOGICAL_OR.equals(cc.getType())) {
            for (Condition sub : cc.getChildren()) {
                if (testAnyCondition(bw, sub)) {
                    return true;
                }
            }
            return false;
        }
        if (Condition.LOGICAL_AND.equals(cc.getType())) {
            for (Condition sub : cc.getChildren()) {
                if (!testAnyCondition(bw, sub)) {
                    return false;
                }
            }
            return true;
        }
        throw new UnsupportedOperationException("not supported condition: " + cc);
    }

    private boolean testUnaryConjectionCondition(BeanWrapper bw, UnaryConjectionCondition ucc) {
        return !testAnyCondition(bw, ucc.getCondition());
    }

    private boolean testExpressionCondition(BeanWrapper bw, ExpressionCondition ec) {
        throw new UnsupportedOperationException("no comparator for type " + ec);
    }
}
